#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <UIKit/UIApplication.h>
#import <LockInfo/Plugin.h>

#import "framework/FWCommon.h"
#import "framework/FWPlugin.h"
#import "framework/FWDotView.h"
#import "framework/FWDetailListEntry.h"
#import "framework/FWDetailListSection.h"

#import "PICommon.h"
#import "PIAlarm.h"
#import "PIPreview.h"
#import "PIAttendee.h"
#import "PIIntegration.h"

#import "PIObject.h"


NSString* const kPIPreviewAlarmsPageLookup = @"-10";

static NSInteger const kNumberElementStar = 9000;
static NSInteger const kNumberElementHash = 9001;
static NSInteger const kNumberElementPause = 9002;


static NSDictionary* sensitivityLookup = [[NSDictionary alloc] initWithObjectsAndKeys:
                                          @"Sensitivity/Confidential", FWNewNumberWithInt(PISensitivityTypeConfidential),
                                          @"Sensitivity/Normal", FWNewNumberWithInt(PISensitivityTypeNormal),
                                          @"Sensitivity/Personal", FWNewNumberWithInt(PISensitivityTypePersonal),
                                          @"Sensitivity/Private", FWNewNumberWithInt(PISensitivityTypePrivate),
                                          nil];


@implementation PIPreview


// ******************************************************************************************
#pragma mark -
#pragma mark Standard entries

- (FWDetailListEntry*)entryForSensitivity:(PISensitivityType)sensitivity bundle:(NSBundle*)bundle
{
    NSString* sensitivityDesc = [sensitivityLookup objectForKey:[NSNumber numberWithInt:sensitivity]];
    if (!sensitivityDesc)
        sensitivityDesc = @"Sensitivity/Normal";

    return [FWDetailListEntry entryWithLocalisedTitle:@"Detail/Sensitivity"
                                       localisedValue:sensitivityDesc
                                               bundle:bundle];
}


// ******************************************************************************************
#pragma mark -
#pragma mark Alarms section

- (NSArray*)alarmsListForObject:(PIObject*)obj dueDate:(NSDate*)dueDate bundle:(NSBundle*)bundle application:(FWApplication*)app
{
    if (obj.alarms) {
        NSMutableArray* alarmsSect = [NSMutableArray arrayWithCapacity:obj.alarms.count];

        NSMutableArray* alarms = [NSMutableArray arrayWithArray:obj.alarms];
        [alarms sortUsingFunction:compareAlarmTimes context:nil];
        
        for (PIAlarm* alarm in alarms) {
            NSMutableString* description = [NSMutableString stringWithCapacity:60];
            if (alarm.type == kPIEventAlarmTypeLocationArrive) {
                [description appendString:[NSString stringWithFormat:localize(bundle, @"Detail/Alarm/Arrive"), alarm.location]];
            }
            else if (alarm.type == kPIEventAlarmTypeLocationLeave) {
                [description appendString:[NSString stringWithFormat:localize(bundle, @"Detail/Alarm/Leave"), alarm.location]];
            }
            else if (alarm.isRelative) {
                NSTimeInterval secs = -alarm.relativeDate;
                if (secs >= SECONDS_PER_DAY) {
                    int days = secs / SECONDS_PER_DAY;
                    secs -= days * SECONDS_PER_DAY;
                    if (days > 0)  
                        [description appendFormat:localize(bundle, days == 1 ? @"Detail/Alarm/Day" : @"Detail/Alarm/Days"), days];
                }

                if (alarm.isFixedTime) {
                    if (description.length == 0) {
                        [description appendString:localize(bundle, @"Detail/Alarm/Same")];
                        description = [NSMutableString stringWithFormat:localize(bundle, @"Detail/Alarm/At"),
                                       description, FWLocalDateStringForUTCDate(alarm.date, FWDateFormatterTimeOnlyFormat, YES, bundle)];
                    }
                    else
                        description = [NSMutableString stringWithFormat:localize(bundle, @"Detail/Alarm/Before"),
                                       description, FWLocalDateStringForUTCDate(alarm.date, FWDateFormatterTimeOnlyFormat, YES, bundle)];
                }
                else {
                    if (secs >= SECONDS_IN_HOUR) {
                        int hours = secs / SECONDS_IN_HOUR;
                        secs -= hours * SECONDS_IN_HOUR;
                        if (description.length > 0)
                            [description appendString:@" "];
                        if (hours > 0)
                            [description appendFormat:localize(bundle, hours == 1 ? @"Detail/Alarm/Hour" : @"Detail/Alarm/Hours"), hours];
                    }
                    if (secs >= SECONDS_IN_MINUTE) {
                        int mins = secs / SECONDS_IN_MINUTE;
                        secs -= mins * SECONDS_IN_MINUTE;
                        if (description.length > 0)
                            [description appendString:@" "];
                        if (mins > 0)
                            [description appendFormat:localize(bundle, mins == 1 ? @"Detail/Alarm/Minute" : @"Detail/Alarm/Minutes"), mins];
                    }
                    if ((secs > 0) && (secs < SECONDS_IN_MINUTE)) {
                        if (description.length > 0)
                            [description appendString:@" "];
                        [description appendFormat:localize(bundle, secs == 1 ? @"Detail/Alarm/Second" : @"Detail/Alarm/Seconds"), (int)secs];
                    }

                    description = [NSMutableString stringWithFormat:localize(bundle, @"Detail/Alarm/Relative"), description];
                }
                
                if (app.fwPlugin.isDebugMode)
                    [description appendFormat:@" (%@)", alarm.date];
            }
            else {
                // Absolute alarm
                BOOL showDate = !FWIsLocalDateSameDay(dueDate, alarm.date);
                [description appendString:FWLocalDateStringForUTCDate(alarm.date, showDate ? FWDateFormatterNormalFormat : FWDateFormatterTimeOnlyFormat, YES, bundle)];

                if (app.fwPlugin.isDebugMode)
                    [description appendFormat:@" (%@)", alarm.date];
            }
            
            NSString* title = localize(bundle, alarms.count == 1 ? @"Detail/Alarm" : @"Detail/Alarms");
            if (alarms.count > 1)
                title = [NSString stringWithFormat:title, alarmsSect.count + 1];
            
            FWDetailListEntry* entry = [FWDetailListEntry entryWithTitle:title value:description];
            if ((alarm.type == kPIEventAlarmTypeLocationArrive) || (alarm.type == kPIEventAlarmTypeLocationLeave))
                [entry withLinkType:[kPIPreviewAlarmsPageLookup stringByAppendingFormat:@"%d", alarmsSect.count]];

            [alarmsSect addObject:entry];
        }
        
        return alarmsSect;
    }

    // No alarms
    return [NSArray array];
}


// ******************************************************************************************
#pragma mark -
#pragma mark Repeat section

- (NSArray*)repeatSectionForObject:(PIObject*)obj bundle:(NSBundle*)bundle application:(FWApplication*)app
{
    if (obj.recurrance) {
        NSMutableArray* repeatSect = [NSMutableArray arrayWithCapacity:2];
        
        FWWeekdayName weekend = (FWWeekdayName)[app.fwPlugin.preferences
                                                intValue:kPIOptionsWeekendStart
                                                withDefault:kPIOptionsWeekendStartDefault];
        
        if (NSString* interval = [obj.recurrance intervalDescriptionWithWeekendStart:weekend])
            [repeatSect addObject:
             [FWDetailListEntry entryWithLocalisedTitle:@"Detail/Repeat/Interval"
                                                  value:interval
                                                 bundle:bundle]];
        if (NSString* until = obj.recurrance.untilDescription)
            [repeatSect addObject:
             [FWDetailListEntry entryWithLocalisedTitle:@"Detail/Repeat/End"
                                                  value:until
                                                 bundle:bundle]];
        
        return repeatSect;
    }
    
    // No info
    return [NSArray array];
}


// ******************************************************************************************
#pragma mark -
#pragma mark Common look and feel functions

- (FWDetailListEntry*)detailColourEntryForTitle:(NSString*)title value:(NSString*)value icon:(NSString*)icon calendarIcon:(NSString*)calendarIcon calendarColor:(UIColor*)calendarColor emojiAfter:(BOOL)emojiAfter
{
    FWDetailListEntry* entry;
    if (value)
        entry = [FWDetailListEntry entryWithTitle:title
                                            value:[PIIntegration stringForTitle:value
                                                                       withIcon:icon
                                                                withProjectIcon:calendarIcon
                                                                     emojiAfter:emojiAfter]];
    else
        entry = [FWDetailListEntry entryWithTitle:[PIIntegration stringForTitle:title
                                                                       withIcon:icon
                                                                withProjectIcon:calendarIcon
                                                                     emojiAfter:emojiAfter]];
    
    if (calendarColor && ![calendarColor isEqual:[UIColor clearColor]]) {
        FWDotView* calIcon = [[[FWDotView alloc] initWithFrame:CGRectMake(0, 0,  18, 18) shadow:YES] autorelease];
        calIcon.color = calendarColor;
        entry.accessoryView = calIcon;
    }
    
    return entry;
}


// ******************************************************************************************
#pragma mark -
#pragma mark Finding telephone number elements

- (NSArray*)numbersFromString:(NSString*)string
{
    if (!string)
        return nil;
    
    NSMutableArray* numbers = [NSMutableArray arrayWithCapacity:10];
    
    NSInteger length = 0;
    BOOL longElementFound = NO;
    NSRange searchRange = NSMakeRange(0, [string length]);
    do {
        NSRange pos = [string rangeOfString:@"\\+?[0-9]+[\\-#*0-9]*[0-9]*" 
                                    options:NSRegularExpressionSearch
                                      range:searchRange];
        if (pos.location == NSNotFound)
            break;
        
        NSString* number = [string substringWithRange:pos];
        [numbers addObject:number];
        length += number.length;
        
        if (number.length > 2)
            longElementFound = YES;
        
        NSInteger searchStart = NSMaxRange(pos);
        searchRange = NSMakeRange(searchStart, [string length] - searchStart);
    } while (TRUE);
    
    if ((length >= 6) && longElementFound && (numbers.count <= 10))
        return numbers;
    else
        return nil;
}

- (NSString*)makePhoneNumber:(NSArray*)numbers
{
    NSMutableString* number = [NSMutableString stringWithCapacity:80];
    
    for (FWDetailListEntry* element in numbers) {
        NSString* entry;
        switch ([(NSNumber*)element.key intValue]) {
            case kNumberElementStar:
                entry = @"*";
                break;
            case kNumberElementHash:
                entry = @"#";
                break;
            case kNumberElementPause:
                entry = @",";
                break;
            default:
                entry = element.title;
                break;
        }

        if (number.length > 0)
            [number appendString:@" "];
        [number appendString:entry];
    }
    
    return [number stringByReplacingOccurrencesOfString:@"-" withString:@""];
}

- (BOOL)buttonClicked:(FWApplication *)application sections:(NSArray *)sections button:(FWDetailListEntry*)button
{
    FWDetailListSection* numberSection = [sections objectAtIndex:1];
    NSString* number = [self makePhoneNumber:numberSection.entries];
    
    NSURL* url = [NSURL URLWithString:[NSString stringWithFormat:@"tel:%@", number]];
    XLog(@"LI:PIEvent: Dialing URL %@", url);
    if ([application.fwPlugin.liPlugin respondsToSelector:@selector(dialNumberNow:)])
        [application.fwPlugin.liPlugin dialNumberNow:number];
    
    return YES;
}

- (NSArray*)dataForNumberListPage:(PIObject*)item application:(FWApplication*)application string:(NSString*)event prompt:(NSString*)prompt
{
    NSBundle* bundle = [NSBundle bundleForClass:[self class]];
    
    NSArray* numbers = [self numbersFromString:event];
    NSMutableArray* numberEntries = [NSMutableArray arrayWithCapacity:numbers.count];
    int index = 0;
    for (NSString* number in numbers) {
        FWDetailListEntry* entry =
        [[FWDetailListEntry entryWithTitle:number
#ifdef DEVELOPMENT_MODE
                                     value:[NSNumber numberWithInt:index++]
#endif
          ]
         withDraggableType:[NSNumber numberWithInt:index++]];
        [numberEntries addObject:entry];
    }
    
    NSMutableArray* controlEntries =
    [NSMutableArray arrayWithObjects:
     [[FWDetailListEntry entryWithLocalisedTitle:@"Detail/Dialer/Star"
#ifdef DEVELOPMENT_MODE
                                           value:[NSNumber numberWithInt:kNumberElementStar]
#endif
                                          bundle:bundle]
      withDraggableType:[NSNumber numberWithInt:kNumberElementStar]],
     [[FWDetailListEntry entryWithLocalisedTitle:@"Detail/Dialer/Hash"
#ifdef DEVELOPMENT_MODE
                                           value:[NSNumber numberWithInt:kNumberElementHash]
#endif
                                          bundle:bundle]
      withDraggableType:[NSNumber numberWithInt:kNumberElementHash]],
     [[FWDetailListEntry entryWithLocalisedTitle:@"Detail/Dialer/Pause"
#ifdef DEVELOPMENT_MODE
                                           value:[NSNumber numberWithInt:kNumberElementPause]
#endif
                                          bundle:bundle]
      withDraggableType:[NSNumber numberWithInt:kNumberElementPause]],
     nil];
    
    
    NSString* number = [self makePhoneNumber:numberEntries];
    NSMutableArray* titleEntries = [NSMutableArray arrayWithCapacity:2];
    if (prompt)
        [titleEntries addObject:
         [[[FWDetailListEntry entryWithTitle:prompt]
           withNoBold]
          withSize:kFWDetailListViewSystemFontSize]];
    [titleEntries addObject:
     [[FWDetailListEntry entryWithTitle:number
                                  value:localize(bundle, @"Detail/Dialer/Dial")]
      withButtonType:nil]];
    
    return [NSArray arrayWithObjects:
            [FWDetailListSection sectionForElements:titleEntries],
            [[FWDetailListSection sectionForElements:numberEntries
                                           withTitle:localize(bundle, @"Detail/Dialer/Number")] 
             withDraggableType],
            [[FWDetailListSection sectionForElements:controlEntries
                                           withTitle:localize(bundle, @"Detail/Dialer/Elements")] 
             withDraggableType],
            [FWDetailListSection sectionForElements:[NSArray array] withTitle:@" "],
            nil];
}


// ******************************************************************************************
#pragma mark -
#pragma mark Numbers page reordering support

- (NSInteger)locationForEntry:(FWDetailListEntry*)entry inSection:(FWDetailListSection*)section
{
    int sourceKey = [(NSNumber*)entry.key intValue];
    int index = 0;
    for (FWDetailListEntry* target in section.entries) {
        int targetKey =[(NSNumber*)target.key intValue];
        if (sourceKey <= targetKey)
            return index;
        ++index;
    }
    
    return -1;
}

- (void)tableReordered:(NSArray*)sections
{
    FWDetailListSection* numberSection = [sections objectAtIndex:1];
    NSString* number = [self makePhoneNumber:numberSection.entries];
    
    FWDetailListSection* titleSection = [sections objectAtIndex:0];
    FWDetailListEntry* numberEntry = [titleSection.entries objectAtIndex:titleSection.entries.count - 1];
    
    numberEntry.title = number;
}

- (BOOL)shouldReloadWhenTableReordered:(NSArray*)sections
{
    return YES;
}

- (BOOL)allowDelete:(FWDetailListEntry*)entry indexPath:(NSIndexPath *)proposedIndexPath
{
    // Only allow deletes from section #1
    return proposedIndexPath.section == 1;
}

- (NSInteger)targetIndexForMove:(FWDetailListEntry*)entry toSection:(FWDetailListSection*)section proposedIndexPath:(NSIndexPath*)proposedIndexPath
{
    if (proposedIndexPath.section == 1) {
        NSInteger key = [(NSNumber*)entry.key intValue];
        
        if ((key >= kNumberElementStar) && (proposedIndexPath.row == 0))
            return 1;
        return proposedIndexPath.row;
    }

    NSInteger index = [self locationForEntry:entry inSection:section];
    if (index < 0)
        return proposedIndexPath.row;
    else
        return index;
}

- (void)movedEntry:(FWDetailListEntry*)entry sections:(NSArray*)sections fromIndexPath:(NSIndexPath*)fromIndexPath toIndexPath:(NSIndexPath*)toIndexPath
{
    NSInteger key = [(NSNumber*)entry.key intValue];
    
    if (key >= kNumberElementStar) {
        if ((fromIndexPath.section == 2) && (toIndexPath.section == 1)) {
            FWDetailListSection* section = [sections objectAtIndex:fromIndexPath.section];
            
            FWDetailListEntry* copy = [entry copy];
            [section.entries insertObject:copy atIndex:fromIndexPath.row];
            [copy release];
        }
        else if ((fromIndexPath.section == 1) && (toIndexPath.section == 2)) {
            FWDetailListSection* section = [sections objectAtIndex:toIndexPath.section];
            [section.entries removeObjectAtIndex:toIndexPath.row];
        }
    }
}

- (void)deletedEntry:(FWDetailListEntry*)entry sections:(NSArray*)sections indexPath:(NSIndexPath*)fromIndexPath
{
    NSInteger key = [(NSNumber*)entry.value intValue];
    
    if (key < kNumberElementStar) {
        FWDetailListSection* section = [sections objectAtIndex:2];
        NSInteger index = [self locationForEntry:entry inSection:section];
        [section.entries insertObject:entry atIndex:index];
    }    
}

@end
